<template>
  <editor-content class="select-none text-base" :editor="editor" />
</template>

<script setup lang="ts">
import { useEditor, EditorContent, Extension } from "@tiptap/vue-3";
import Document from "@tiptap/extension-document";
import Paragraph from "@tiptap/extension-paragraph";
import Text from "@tiptap/extension-text";
import Placeholder from "@tiptap/extension-placeholder";
import { Mark } from "@tiptap/core";
import History from "@tiptap/extension-history";
interface Props {
  placeholder?: string;
  disableEnter?: boolean;
  id: string;
  fontSize?: number;
}
const props = defineProps<Props>();
const modelValue = defineModel<string | Object>({ default: "" });
const lineHeight = ref(1.8);
const { aiChatInputShow } = useAISelectionBar();
const { setAiSelectionBarShow, setAiChatInputShow } = useAISelectionBar();

const defaultPlaceholder = computed(() => props.placeholder ?? "开始输入...");




const Span = Mark.create({
  name: "span",
  parseHTML() {
    return [
      {
        tag: "span",
        getAttrs: (node) => {
          if (typeof node === "string") return {};
          return {
            class: node.getAttribute("class"),
          };
        },
      },
    ];
  },
  renderHTML({ HTMLAttributes }) {
    return ["span", { ...HTMLAttributes, class: "select-data" }, 0];
  },
});

const SpanActive = Mark.create({
  name: "spanActive",
  parseHTML() {
    return [
      {
        tag: "span",
        getAttrs: (node) => {
          if (typeof node === "string") return {};
          return {
            class: node.getAttribute("class"),
          };
        },
      },
    ];
  },
  renderHTML({ HTMLAttributes }) {
    return ["span", { ...HTMLAttributes, class: "select-data-active" }, 0];
  },
});

const { wordCount } = useEditorAgent();
const updateWordCount = (editor: any) => {
  if (props.disableEnter) return;
  const text = editor.getText();
  wordCount.value = text.trim() ? text.trim().split(/\s+/).join().length : 0;
};

const { setAiSelectionBarPosition, setAiSelectionBarPositionHandler } =
  useAISelectionBar();
const showAIBarOnSelection = () => {
  const { from } = editor.value!.state.selection;
  const pos = editor.value!.view.coordsAtPos(from);
  setAiSelectionBarPosition({
    y: pos.top - 40,
    x: pos.left,
    type: "top",
  });
};

const shortcutkeys = Extension.create({
  name: "customKeymap",
  addKeyboardShortcuts() {
    return {
      "Mod-k": () => {
        if (aiChatInputShow.value) return false;
        setAiSelectionBarShow(true);
        setAiChatInputShow(true);
        showAIBarOnSelection();
        return true;
      },
    };
  },
});

const editor = useEditor({
  content: modelValue.value,
  extensions: [
    Document,
    Paragraph,
    Text,
    Span,
    SpanActive,
    Placeholder.configure({
      placeholder: defaultPlaceholder.value,
    }),
    History,
    shortcutkeys,
  ],
  editable: true,
  injectCSS: false,
  onSelectionUpdate: ({ editor }) => {
    const selectionText = editor.state.doc.textBetween(
      editor.state.selection.from,
      editor.state.selection.to
    );

    if (!selectionText) {
      !aiChatInputShow.value && resetAiSelectionBarPosition();
      editor
        .chain()
        .command(({ tr }) => {
          const mark = editor.schema.marks.span;
          if (!mark) return false;
          tr.setMeta("addToHistory", false);
          editor.state.doc.descendants((node, pos) => {
            if (node.isText) {
              tr.removeMark(pos, pos + node.nodeSize, mark);
            }
          });
          return true;
        })
        .run();
      return;
    }
    const { from, to } = editor.state.selection;
    editor
      .chain()
      .command(({ tr }) => {
        const mark = editor.schema.marks.span;
        if (!mark) return false;
        tr.setMeta("addToHistory", false);
        editor.state.doc.descendants((node, pos) => {
          if (node.isText) {
            tr.removeMark(pos, pos + node.nodeSize, mark);
          }
        });

        tr.addMark(from, to, mark.create());
        return true;
      })
      .run();
  },
  onUpdate: ({ editor }) => {
    updateModelValue(editor)
    updateWordCount(editor);
  },
  autofocus: true,
});

const resetAiSelectionBarPosition = () => {
  setAiSelectionBarShow(false);
};

const updateModelValue = (editor: any) => {
  if (!editor) return;
  const newText = editor.getText();
  const newJSON = editor.getJSON();
  if (props.disableEnter) {
    const textWithoutNewlines = newText.replace(/\n/g, "");
    modelValue.value = textWithoutNewlines;
    return;
  }

  modelValue.value = newJSON;
};

onMounted(() => {
  if (!editor.value) return;
  const tiptapContainer = document.querySelector(".tiptap-container");
  if (!tiptapContainer) return;
  tiptapContainer.addEventListener("mouseup", onMouseUp);
  tiptapContainer.addEventListener("scroll", setAiSelectionBarPositionHandler);
  updateWordCount(editor.value);
  removeAllSpanActive();
});

onBeforeUnmount(() => {
  const tiptapContainer = document.querySelector(".tiptap-container");
  if (!tiptapContainer) return;

  tiptapContainer.removeEventListener(
    "scroll",
    setAiSelectionBarPositionHandler
  );
  tiptapContainer.removeEventListener("mouseup", onMouseUp);
});

const onMouseUp = () => {
  if (aiChatInputShow.value) return;
  const selectionText = editor.value?.state.doc.textBetween(
    editor.value.state.selection.from,
    editor.value.state.selection.to
  );
  if (selectionText) {
    setAiSelectionBarShow(true);
  }
  setAiSelectionBarPositionHandler();
};

const { removeAllSpanActive } = useEditorAgent();

defineExpose({
  setContent: async (content: string) => {
    editor.value?.commands.setContent(content, false);
    await nextTick();
    removeAllSpanActive();
    updateWordCount(editor.value);
    setAiSelectionBarShow(false);
  },
  editor,
});
</script>

<style>
.ProseMirror {
  @apply select-none;
  outline: none !important;
  caret-color: var(--color-caret);
  overflow: auto;
  display: table;
  flex-direction: column;
  width: 100%;
  font-size: v-bind(fontSize + "px");
  white-space: pre-wrap;
}

.select-data-active {
  background-color: var(--color-bg-selection-richEditor-active);
  color: #000;
}

.select-data>.select-data-active {
  background-color: var(--color-bg-selection-richEditor) !important;
}

.select-data {
  background-color: var(--color-bg-selection-richEditor) !important;
  color: #000;
}

.ProseMirror p {
  line-height: v-bind(lineHeight);
  color: var(--color-textColor-selection-richEditor);
}

.ProseMirror ::selection {
  background: transparent;
}

.ProseMirror p.is-editor-empty:first-child::before {
  color: var(--color-text-placeholder);
  content: attr(data-placeholder);
  float: left;
  height: 0;
  pointer-events: none;
}
</style>
